//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
//
// Use of this sample source code is subject to the terms of the Microsoft
// license agreement under which you licensed this sample source code. If
// you did not accept the terms of the license agreement, you are not
// authorized to use this sample source code. For the terms of the license,
// please see the license agreement between you and Microsoft or, if applicable,
// see the LICENSE.RTF on your install media or the root of your tools installation.
// THE SAMPLE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES OR INDEMNITIES.
//
//////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2003 Intrinsyc Europe Ltd.
//////////////////////////////////////////////////////////////////////////////
// Copyright (C) 2004 Siemens AG
//
// Version: 1.1.2.5
// Date:    11. Jan. 2006
//////////////////////////////////////////////////////////////////////////////
//
//
// (C) Copyright 2007 Marvell International Ltd.
//  All Rights Reserved
//


#include "precomp.h"

#define RESP_DATA_BUF 1024
#define RIL_TIMEOUT_MS   2000
#define RIL_DATAMODE_CMD_DELAY_TIME 3000
#define RIL_MAX_DATAMODE_CMD_DELAY_TIME 30000

#define NETWORK_USSD_TIMEOUT 20000      // Timeout for network initiated USSD commands

//
// Globals
//

// Command Queues
CPriorityQueue<CCommand, COMMAND_QUEUE_SIZE>* g_pCmdQ[NUMBER_OF_PORTS];
// Response Queues
CQueue<CResponse, RESPONSE_QUEUE_SIZE>* g_pRspQ[NUMBER_OF_PORTS];
// Data buffer for blocking queue functions
QUEUE_BLOCKING_DATA g_QBlockingData[NUMBER_OF_PORTS];


extern RHA_FUNCTION_LIST RHAFunctionList;
extern BOOL g_fGPRSActive;
extern BOOL g_fAudioPathActivated;
extern BOOL g_fReleaseHeldCall;
#ifdef RIL_FAKECSQ
extern volatile BOOL g_fFakeSignalStrength;
#endif

BOOL g_bLastCmdPIN2 = FALSE;
extern BOOL g_fCallWaiting;
extern BOOL g_fEmergencyCall;
#define MAX_TRACKED_CALL_ID             10
extern BOOL g_rgfCallsInProgress[MAX_TRACKED_CALL_ID];

//////////////////////////////////////////////////////////////////////////////
//
// Internal functions
//
//////////////////////////////////////////////////////////////////////////////

//
//
//
DWORD WINAPI RequeueThreadProc(LPVOID lpParameter)
{
    TBD_FUNCTION(RequeueThreadProc);
    REQUEUE_THREAD_DATA* prtd = (REQUEUE_THREAD_DATA*)lpParameter;
    TBD_ASSERT(NULL != prtd);
    TBD_ASSERT(NULL != prtd->pCmd);
    TBD_ASSERT((prtd->PortType >= 0) && (prtd->PortType < NUMBER_OF_PORTS));

    g_PowerManager.WaitCmdRequeue(prtd->dwDelay);

    // Place the command into the command queue
    //    (this may block if the queue is full)
    if (!g_pCmdQ[prtd->PortType]->Put(prtd->pCmd, INFINITE))
    {
        delete prtd->pCmd;
    }
    prtd->pCmd = NULL;
    delete prtd;
    return 0;
}

//
//
//
BOOL RequeueCmdWithDelay(COM_PORT_TYPE PortType, CCommand* pCmd, DWORD dwDelay) {
    //TBD_FUNCTION(RequeueCmdWithDelay);
    TBD_ASSERT(NULL != pCmd);
    //TBD_ASSERT(TRUE == pCmd->FIgnoreRsp());

    REQUEUE_THREAD_DATA* prtd = NULL;
    HANDLE hThread;
    CCommand* pCmdNew = NULL;
    DWORD dwThreadID;
    BOOL fRet = FALSE;

    EnterCriticalSection(&g_csClosing);

    // Allocate parameter structure to be passed to the thread proc
    prtd = new REQUEUE_THREAD_DATA;
    if (!prtd)
    {
        TBD_OUTPUT(TBDCT_INFO, TBDOL_ERROR, TEXT("RequeueCmdWithDelay : Unable to construct REQUEUE_THREAD_DATA"));
        goto Error;
    }

    // Set up a command to be sent
    pCmdNew = new CCommand;
    if (!pCmdNew || !pCmdNew->Init(pCmd, TRUE))
    {
        TBD_OUTPUT(TBDCT_INFO, TBDOL_ERROR, TEXT("RequeueCmdWithDelay : Unable to construct or Init CCommand"));
        goto Error;
    }

    if (pCmdNew->GetHandle())
    {
        if (!pCmdNew->GetHandle()->GetCmdList()->Add(pCmdNew))
        {
            goto Error;
        }
    }

    prtd->pCmd = pCmdNew;
    prtd->dwDelay = dwDelay;
    prtd->PortType = PortType;
    hThread = CreateThread(NULL, 0, RequeueThreadProc, (LPVOID)prtd, 0, &dwThreadID);
    if (!hThread)
    {
        TBD_OUTPUT(TBDCT_INFO, TBDOL_ERROR, TEXT("RequeueCmdWithDelay : Unable to CreateThread(RequeueThreadProc)"));
        goto Error;
    }
    CloseHandle(hThread);
    fRet = TRUE;

    Error:
    if (!fRet)
    {
        delete prtd;
        delete pCmdNew;
    }

    LeaveCriticalSection(&g_csClosing);

    return fRet;
}

//
// Calculate the timing information for a command
//
void CalculateCmdTiming(CRilHandle* const pRilDevice, const APIID apiid, DWORD dwInitTimeout, DWORD* pdwTimeout) {
    DWORD dwTimeout = dwInitTimeout;

    if (pRilDevice && (apiid != APIID_NONE) && !dwTimeout) {
      // Get the timing info about this API
      APIINFO apiiInfo;
      pRilDevice->GetAPIInfo(apiid, apiiInfo);
      dwTimeout = apiiInfo.dwTimeout;
    } else {
      // Get the default timing info
      if (!dwTimeout) {
        dwTimeout = g_TimeoutAPIDefault;
      }
    }

    // Extend the timeout to account for CTS polling (+CFUN=7)
    if (dwTimeout != INFINITE) {
        //dwTimeout += 2000;
        dwTimeout += RIL_TIMEOUT_MS;
    }

    if (pdwTimeout) *pdwTimeout = dwTimeout;
}

//
// Place a command into the Command Queue
//
BOOL QueueCmdWithTimeout(COM_PORT_TYPE PortType, CRilInstanceHandle* const pHandle, const LPCSTR szCmd, const LPCSTR szCmdPart2, const DWORD dwOptions, const APIID apiid,
              const PFN_CMD_PARSE pfnParse, CNotificationData* const pnd, HRESULT& rhrCmdID, DWORD dwTimeout, DWORD dwRetries, DWORD dwRetryDelay,
              const PFN_CMD_ERROR pfnError, const LPVOID pParseParam, const CME_TRANSLATION *pCMET, const PFN_PRE_CMD_CHECK pfnPreCheck) {
    //TBD_FUNCTION(QueueCmdWithTimeout);
    TBD_ASSERT(0 != (dwOptions & CMDOPT_NOOP) || NULL != szCmd);

    DWORD               dwUsedTimeout;
    CRilHandle*         pRilDevice = pHandle->GetDevice();
    CCommand*           pCmd = NULL;
    CNotificationData*  pndNotify = pnd;
    BOOL                fRet = FALSE;

    TBD_ASSERT(NULL != pRilDevice);

    CalculateCmdTiming(pRilDevice, apiid, dwTimeout, &dwUsedTimeout);

    //TBD_OUTPUT(TBDCT_INFO, TBDOL_TRACE, TEXT("QueueCmdWithTimeout : timeout: %d msec\n"), dwUsedTimeout);    

    // Set up a command to be sent
    pCmd = new CCommand;
    if (!pCmd)
    {
        TBD_OUTPUT(TBDCT_INFO, TBDOL_ERROR, TEXT("QueueCmdWithTimeout : Unable to construct CCommand"));
        goto Error;
    }
    if (!pCmd->Init(pHandle, szCmd, szCmdPart2, dwOptions, dwUsedTimeout, pndNotify, pfnParse, dwRetries, dwRetryDelay, apiid, pfnError, pParseParam, pCMET, pfnPreCheck))
    {
        TBD_OUTPUT(TBDCT_INFO, TBDOL_ERROR, TEXT("QueueCmdWithTimeout : Unable to init CCommand"));
        goto Error;
    }

    pndNotify = NULL;

    // Add this command to the handle's command list
    pHandle->GetCmdList()->Add(pCmd);

    rhrCmdID = pCmd->GetID();

    //TBD_OUTPUT(TBDCT_INFO, TBDOL_TRACE, TEXT("Queueing command with ID: 0x%08x"), rhrCmdID);

    if (dwOptions & CMDOPT_DIAL)
    {
        pHandle->GetDevice()->SetIgnoreNextNOCARRIER(FALSE);
    }
#ifdef AUXCOM_PORT
    else if (dwOptions & CMDOPT_HANGUP && PortType == COMMAND_PORT)
    {
        pHandle->GetDevice()->SetIgnoreNextNOCARRIER(TRUE);
    }
#endif

    if ((dwOptions&CMDOPT_NOOP)==0 && (dwOptions&CMDOPT_CPIN2NEUTRAL)==0)
    {
        if ((dwOptions&CMDOPT_CPIN2RELATED)!=0 && PortType==COMMAND_PORT)
        {
            g_bLastCmdPIN2 = TRUE;
        }
        else
        {
            g_bLastCmdPIN2 = FALSE;
        }
    }

    // Place the command into the command queue
    //    (this may block if the queue is full)
    if (!g_pCmdQ[PortType]->Put(pCmd, INFINITE))
    {
        TBD_OUTPUT(TBDCT_INFO, TBDOL_ERROR, TEXT("QueueCmdWithTimeout : Unable to Put CCommand"));
        goto Error;
    }


    if (pCmd->FHangup() || pCmd->FDial() || pCmd->FAnswer())
    {
        SetEvent(g_hNewDialOrHangupEvent);
    }

    pCmd = NULL;
    fRet = TRUE;

    Error:
    if (!fRet)
    {
        delete pCmd;
        delete pndNotify;
    }
    return fRet;
}

//
// Place an internal command into the command queue
//
BOOL QueueInternalCmd(COM_PORT_TYPE PortType, CRilHandle* const pRilDevice, const LPCSTR szCmd, const DWORD dwOptions, const APIID apiid, DWORD dwTimeout, DWORD dwRetries, DWORD dwRetryDelay,
                      const PFN_CMD_PARSE pfnParse, const PFN_CMD_ERROR pfnError, const LPVOID pParseParam, CNotificationData* const pnd) {
    //TBD_FUNCTION(QueueInternalCmd);
    TBD_ASSERT(0 != (dwOptions & CMDOPT_NOOP) || NULL != szCmd);

    DWORD               dwUsedTimeout;
    CCommand*           pCmd = NULL;
    CNotificationData*  pndNotify = pnd;
    BOOL                fRet = FALSE;

    CalculateCmdTiming(pRilDevice, apiid, dwTimeout, &dwUsedTimeout);

    // Set up a command to be sent
    pCmd = new CCommand;
    if (!pCmd)
    {
        TBD_OUTPUT(TBDCT_INFO, TBDOL_ERROR, TEXT("QueueInternalCmd : Unable to construct CCommand"));
        goto Error;
    }
    if (!pCmd->Init(NULL, szCmd, NULL, dwOptions, dwUsedTimeout, pndNotify, pfnParse, dwRetries, dwRetryDelay, apiid, pfnError, pParseParam, NULL))
    {
        TBD_OUTPUT(TBDCT_INFO, TBDOL_ERROR, TEXT("QueueInternalCmd : Unable to init CCommand"));
        goto Error;
    }

    pndNotify = NULL;

    // Place the command into the command queue
    //    (this may block if the queue is full)
    if (!g_pCmdQ[PortType]->Put(pCmd, INFINITE))
    {
        TBD_OUTPUT(TBDCT_INFO, TBDOL_ERROR, TEXT("QueueInternalCmd : Unable to Put CCommand"));
        goto Error;
    }

    pCmd = NULL;
    fRet = TRUE;

    Error:
    if (!fRet)
    {
        delete pCmd;
        delete pndNotify;
    }
    return fRet;
}

//
// Parsing callback for blocking command queueing functions
//
HRESULT ParseUnblockErr(LPCSTR szRsp, LPVOID pParam) {
  COM_PORT_TYPE  PortType = (COM_PORT_TYPE)pParam;
  HRESULT        Ret = S_OK;

  if (g_QBlockingData[PortType].pfnParseErr && szRsp) {
    Ret = g_QBlockingData[PortType].pfnParseErr(szRsp, g_QBlockingData[PortType].pParam);
  }
  SetEvent(g_QBlockingData[PortType].hEvents[1]);
  return Ret;
}

//
// Parsing callback for blocking command queueing functions
//
HRESULT ParseUnblockOk(LPCSTR szRsp, void*& pBlob, UINT& cbBlob, LPVOID pParam) {
  COM_PORT_TYPE  PortType = (COM_PORT_TYPE)pParam;
  HRESULT        Ret = S_OK;

  if (g_QBlockingData[PortType].pfnParseOk) {
    Ret = g_QBlockingData[PortType].pfnParseOk(szRsp, pBlob, cbBlob, g_QBlockingData[PortType].pParam);
  }
  SetEvent(g_QBlockingData[PortType].hEvents[SUCCEEDED(Ret) ? 0 : 1]);
  return Ret;
}

//
// Place an internal command into the command queue and block until the command has been executed
//
BOOL QueueInternalCmdBlocking(COM_PORT_TYPE PortType, CRilHandle* const pRilDevice, const LPCSTR szCmd, const DWORD dwOptions, const APIID apiid, DWORD dwTimeout, DWORD dwRetries, DWORD dwRetryDelay,
                              const PFN_CMD_PARSE pfnParse, const PFN_CMD_ERROR pfnError, const LPVOID pParseParam, CNotificationData* const pnd) {
    //TBD_FUNCTION(QueueInternalCmdBlocking);
    DWORD  dwBlockTimeout;
    BOOL   fRet = FALSE;

    CalculateCmdTiming(pRilDevice, apiid, dwTimeout, &dwBlockTimeout);
    if (dwRetries) {
      dwBlockTimeout = (dwBlockTimeout + dwRetryDelay) * (dwRetries + 1);
    }
    dwBlockTimeout += 1000;

    // Store data passed to parsing functions
    SYNCBLOCK(g_QBlockingData[PortType].csLock);
    g_QBlockingData[PortType].pfnParseOk = pfnParse;
    g_QBlockingData[PortType].pfnParseErr = pfnError;
    g_QBlockingData[PortType].pParam = pParseParam;
    ResetEvent(g_QBlockingData[PortType].hEvents[0]);
    ResetEvent(g_QBlockingData[PortType].hEvents[1]);

    // Queue the command
    if (QueueInternalCmd(PortType, pRilDevice, szCmd, dwOptions, apiid, dwTimeout, dwRetries, dwRetryDelay, ParseUnblockOk, ParseUnblockErr, (LPVOID)PortType, pnd)) {
      if (WaitForMultipleObjects(2, g_QBlockingData[PortType].hEvents, FALSE, dwBlockTimeout) == WAIT_OBJECT_0) {
        fRet = TRUE;
      }
    }

    return fRet;
}

//
// Place a command into the Command Queue and block until the command has been executed
//
BOOL QueueCmdBlocking(COM_PORT_TYPE PortType, CRilInstanceHandle* const pHandle, const LPCSTR szCmd, const DWORD dwOptions, const APIID apiid, const PFN_CMD_PARSE pfnParse, CNotificationData* const pnd,
              HRESULT& rhrCmdID, DWORD dwTimeout, DWORD dwRetries, DWORD dwRetryDelay, const PFN_CMD_ERROR pfnError, const LPVOID pParseParam) {
    //TBD_FUNCTION(QueueCmdBlocking);
    DWORD  dwBlockTimeout;
    BOOL   fRet = FALSE;

    // Check input parameter
    if (!pHandle) {
      return FALSE;
    }

    CalculateCmdTiming(pHandle->GetDevice(), apiid, dwTimeout, &dwBlockTimeout);
    if (dwRetries) {
      dwBlockTimeout = (dwBlockTimeout + dwRetryDelay) * (dwRetries + 1);
    }
    dwBlockTimeout += 1000;

    // Store data passed to parsing functions
    SYNCBLOCK(g_QBlockingData[PortType].csLock);
    g_QBlockingData[PortType].pfnParseOk = pfnParse;
    g_QBlockingData[PortType].pfnParseErr = pfnError;
    g_QBlockingData[PortType].pParam = pParseParam;
    ResetEvent(g_QBlockingData[PortType].hEvents[0]);
    ResetEvent(g_QBlockingData[PortType].hEvents[1]);

    // Queue the command
    if (QueueCmdWithTimeout(PortType, pHandle, szCmd, NULL, dwOptions, apiid, ParseUnblockOk, pnd, rhrCmdID, dwTimeout, dwRetries, dwRetryDelay, ParseUnblockErr, (LPVOID)PortType)) {
      if (WaitForMultipleObjects(2, g_QBlockingData[PortType].hEvents, FALSE, dwBlockTimeout) == WAIT_OBJECT_0) {
        fRet = TRUE;
      }
    }

    return fRet;
}

//
// Place a command into the Command Queue
//
BOOL QueueCmd(COM_PORT_TYPE PortType, CRilInstanceHandle* const pHandle, const LPCSTR szCmd, const DWORD dwOptions, const APIID apiid,
              const PFN_CMD_PARSE pfnParse, CNotificationData* const pnd, HRESULT& rhrCmdID, const PFN_CMD_ERROR pfnError, const LPVOID pParseParam)
{
    return QueueCmdWithTimeout(PortType, pHandle, szCmd, NULL, dwOptions, apiid, pfnParse, pnd, rhrCmdID, 0, 0, 0, pfnError, pParseParam);
}

//
// Place a command into the Command Queue
//
BOOL QueueCmdPriority(CRilInstanceHandle* const pHandle, const LPCSTR szCmd, const DWORD dwOptions, const APIID apiid,
              const PFN_CMD_PARSE pfnParse, CNotificationData* const pnd, HRESULT& rhrCmdID, const PFN_CMD_ERROR pfnError,
              const LPVOID pParseParam, const CME_TRANSLATION *pCMET, const PFN_PRE_CMD_CHECK pfnPreCheck, DWORD dwTimeout)
{
    // To avoid, that two thread try to issue a priority command at the same time, we serialize the calls by using
    // a critical section.
    SYNCBLOCK(g_ComManager.csKeepOrder);
    return QueueCmdWithTimeout(ORDER_PRIORITY_PORT, pHandle, szCmd, NULL, dwOptions|CMDOPT_KEEPORDER, apiid, pfnParse, pnd, rhrCmdID, dwTimeout, 0, 0, pfnError, pParseParam, pCMET, pfnPreCheck);
}

//
//
//
BOOL QueueCmdWithRetry(COM_PORT_TYPE PortType, CRilInstanceHandle* const pHandle, const LPCSTR szCmd, const DWORD dwOptions, const APIID apiid,
              const PFN_CMD_PARSE pfnParse, CNotificationData* const pnd, HRESULT& rhrCmdID, DWORD dwRetries, DWORD dwRetryDelay, const PFN_CMD_ERROR pfnError, const LPVOID pParseParam)
{
    return QueueCmdWithTimeout(PortType, pHandle, szCmd, NULL, dwOptions, apiid, pfnParse, pnd, rhrCmdID, 0, dwRetries, dwRetryDelay, pfnError, pParseParam);
}

//
// Place a multipart command into the Command Queue
//
BOOL QueueMultipartCmd(COM_PORT_TYPE PortType, CRilInstanceHandle* const pHandle, const LPCSTR szCmd, const LPCSTR szCmdPart2, const DWORD dwOptions, const APIID apiid,
              const PFN_CMD_PARSE pfnParse, CNotificationData* const pnd, HRESULT& rhrCmdID, const PFN_CMD_ERROR pfnError, const LPVOID pParseParam)
{
    return QueueCmdWithTimeout(PortType, pHandle, szCmd, szCmdPart2, dwOptions, apiid, pfnParse, pnd, rhrCmdID, 0, 0, 0, pfnError, pParseParam);
}

//
// Place a multipart command into the Command Queue
//
BOOL QueueMultipartCmdWithRetry(COM_PORT_TYPE PortType, CRilInstanceHandle* const pHandle, const LPCSTR szCmd, const LPCSTR szCmdPart2, const DWORD dwOptions, const APIID apiid,
              const PFN_CMD_PARSE pfnParse, CNotificationData* const pnd, HRESULT& rhrCmdID, const PFN_CMD_ERROR pfnError, const LPVOID pParseParam, DWORD dwRetries, DWORD dwRetryDelay)
{
    return QueueCmdWithTimeout(PortType, pHandle, szCmd, szCmdPart2, dwOptions, apiid, pfnParse, pnd, rhrCmdID, 0, dwRetries, dwRetryDelay, pfnError, pParseParam);
}

//
// Place a command into the Command Queue
//
BOOL QueueCmdCMET(COM_PORT_TYPE PortType, CRilInstanceHandle* const pHandle, const LPCSTR szCmd, const DWORD dwOptions, const APIID apiid,
              const PFN_CMD_PARSE pfnParse, CNotificationData* const pnd, HRESULT& rhrCmdID, const CME_TRANSLATION *pCMET)
{
    return QueueCmdWithTimeout(PortType, pHandle, szCmd, NULL, dwOptions, apiid, pfnParse, pnd, rhrCmdID, 0, 0, 0, 0, 0, pCMET);
}

//
//
//
BOOL QueueRsp(COM_PORT_TYPE PortType, CRilHandle* pRilDevice, CResponse*& rpRsp)
{
    //TBD_FUNCTION(QueueRsp);
    BOOL fRet = FALSE;

    // Remember that we're not waiting for AT response anymore
    pRilDevice->StopWaitingForRsp(PortType);

    // Queue the command response
    if (!g_pRspQ[PortType]->Put(rpRsp, INFINITE))
    {
        TBD_OUTPUT(TBDCT_INFO, TBDOL_ERROR, TEXT("QueueRsp : Unable to Put CResponse"));
        goto Error;
    }
    rpRsp = NULL;

    fRet = TRUE;

    Error:
    return fRet;
}

//
//
//
DWORD CRilHandle::CommandThread(COM_PORT_TYPE PortType)
{
    //TBD_FUNCTION(CRilHandle::DataCommandThread);
    CCommand* pCmd = NULL;
    BOOL fDummy;
    HRESULT hr;

    TBD_ASSERT(m_pComDevice[PortType] != NULL);

    // Tell the main thread that we've reached the checkpoint
    GetCheckPoint()->Reached();

    while (1)
    {
        DWORD dwTimeOut;


        RILRetailTrace((TEXT("CRilHandle::CommandThread WaitForCommandOrCancel ++++++++++++++++++++++++++++++\n")));
        // Make sure the module is prepared to handle the command before sending it (except for init commands)
        if (!WaitForCommandOrCancel(PortType))
        {
            TBD_OUTPUT(TBDCT_INFO, TBDOL_ERROR, TEXT("CRilHandle::CommandThread : Cancel was set"));
            goto Error;
        }
        RILRetailTrace((TEXT("CRilHandle::CommandThread WaitForCommandOrCancel -------------------------------\n")));
        
        // When the module is in the USSD answer mode, the AT command interface is blocked.
        // There is nothing else to do, than to wait until it is unblocked.
        dwTimeOut = 0;
        while (m_fUSSDAnswerMode[PortType] && (dwTimeOut < NETWORK_USSD_TIMEOUT))
        {
            Sleep(100);
            dwTimeOut += 100;
        }
        // Still blocked, we cancel the mode the hard way.
        if (m_fUSSDAnswerMode[PortType])
        {
            CancelUSSDAnswerMode(PortType);
        }

        // Get the next command from the Queue
        RILRetailTrace((TEXT("CRilHandle::CommandThread g_pCmdQ Get ++++++++++++++++++++++++++++++++\n")));        
        hr = g_pCmdQ[PortType]->Get(pCmd, INFINITE);
        RILRetailTrace((TEXT("CRilHandle::CommandThread g_pCmdQ Get  -------------------------------\n")));
        
        if (RIL_E_CANCELLED == hr)
        {
            TBD_OUTPUT(TBDCT_INFO, TBDOL_ERROR, TEXT("CRilHandle::CommandThread : Get returned RIL_E_CANCELLED (Error)"));
            delete pCmd;
            m_pComDevice[PortType]->UnlockRILCmd();
            goto Error;
        }


        // If we failed initialization, ignore any non-init commands in the queue
        if (m_fFailedInit && !pCmd->FInit())
        {
            HRESULT dwError = RIL_E_RADIOFAILEDINIT;
            pCmd->SendResponse(RIL_RESULT_ERROR, &dwError, sizeof(HRESULT));
            delete pCmd;
            m_pComDevice[PortType]->UnlockRILCmd();
            continue;
        }

        RILRetailTrace((TEXT("CRilHandle::CommandThread SendRILCmdHandleRsp  ++++++++++++++++++++++++++++++++\n")));        

        // Send the command to comm port
        if (!m_pComDevice[PortType]->SendRILCmdHandleRsp(this, pCmd, fDummy, fDummy))
        {
            TBD_OUTPUT(TBDCT_INFO, TBDOL_ERROR, TEXT("CRilHandle::CommandThread : SendRILCmdHandleRsp failed (Error)"));
            // Don't give up and exit, just try again
            continue;
        }
        RILRetailTrace((TEXT("CRilHandle::CommandThread SendRILCmdHandleRsp -----------------------------------\n")));        
        
        if (m_pComDevice[PortType]->FDataMode())
        {
            m_pComDevice[PortType]->WaitForCommandMode();
        }


    }

    Error:
    delete pCmd;
    g_RilLog.LogEvent(RILLOG_ZONE_CMD, RILLOG_EVENT_COMMANDTHREADEXIT);
    return 0;
}

//
//
//
DWORD CRilHandle::ResponseThread(COM_PORT_TYPE PortType)
{
    //TBD_FUNCTION(CRilHandle::ResponseThread);
    char   szData[RESP_DATA_BUF];
    DWORD  dwRead;
    DWORD  i;
    BOOL   fBufferFull;

    // Tell the main thread that we've reached the checkpoint
    GetCheckPoint()->Reached();

    while (1)
    {
        // See if the thread needs to terminate
        if (!WaitForResponseOrCancel(PortType))
        {
            break;
        }

        do
        {
            if (!m_pComDevice[PortType]->VirtRead(szData, RESP_DATA_BUF, dwRead))
            {
                TBD_OUTPUT(TBDCT_INFO, TBDOL_ERROR, TEXT("CRilHandle::ResponseThread : Read failed"));
                break;
            }

            if (!dwRead)
            {
                TBD_OUTPUT(TBDCT_INFO, TBDOL_INFORMATION, TEXT("CRilHandle::ResponseThread no data?!"));
                break;
            }

            fBufferFull = ((dwRead == RESP_DATA_BUF) ? TRUE : FALSE);

            if ((dwRead > 1) && !szData[0])
            {
                for (i = 0; i < (dwRead - 1); i++)
                {
                    szData[i] = szData[i + 1];
                }
                dwRead--;
            }

            if (!HandleRxData(PortType, szData, dwRead))
            {
                TBD_OUTPUT(TBDCT_INFO, TBDOL_ERROR, TEXT("CRilHandle::ResponseThread: HandleRxData failed?!"));
                return 0;
            }
        } while (fBufferFull);
    }

    return 0;
}

//
// Thread responsible for sending commands from the Command Queue to COM port
//
DWORD WINAPI CmdThreadProc(LPVOID lpParameter)
{
    //TBD_FUNCTION(CmdThreadProc);
    TBD_ASSERT(lpParameter != NULL);
    QUEUE_THREAD_DATA* pData = (QUEUE_THREAD_DATA*)lpParameter;
    TBD_ASSERT(pData->pRilDevice != NULL);
    CRilHandle* pRilDevice = pData->pRilDevice;
    COM_PORT_TYPE PortType = pData->PortType;
    delete pData;
    return pRilDevice->CommandThread(PortType);
}

//
// Thread responsible for reading responses from COM port into the Response Queue
//
DWORD WINAPI ReadThreadProc(LPVOID lpParameter)
{
    //TBD_FUNCTION(ReadThreadProc);
    TBD_ASSERT(lpParameter != NULL);
    QUEUE_THREAD_DATA* pData = (QUEUE_THREAD_DATA*)lpParameter;
    TBD_ASSERT(pData->pRilDevice != NULL);
    CRilHandle* pRilDevice = pData->pRilDevice;
    COM_PORT_TYPE PortType = pData->PortType;
    delete pData;

    // Switch the thread into higher priority (to guarantee that the module's in buffer doesn't get overflown)
    if (!SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_ABOVE_NORMAL))
    {
        TBD_OUTPUT(TBDCT_INFO, TBDOL_ERROR, TEXT("ReadThreadProc : Unable to raise priority of read thread!!"));
        return 0;
    }

    return pRilDevice->ResponseThread(PortType);
}

//
// Handle the received data
//
BOOL CRilHandle::HandleRxData(COM_PORT_TYPE PortType, char *szData, DWORD dwRead)
{
    LPSTR szAppend = NULL;
    LPSTR szRemainder = NULL;
    UINT cbAppend;
    UINT cbRemainder;
    void* pBlob;
    UINT cbBlob;
    DWORD i;
    CResponse* pRsp = NULL;
    BOOL bSuccess=FALSE;

    // TBD_OUTPUT(TBDCT_INFO, TBDOL_INFORMATION, TEXT("ResponseThread read %s"), TString(PrintableString(szData, dwRead)));

    if (!AppendReadBytes(PortType, szData, dwRead))
    {
        TBD_OUTPUT(TBDCT_INFO, TBDOL_ERROR, TEXT("CRilHandle::ResponseThread : AppendReadBytes failed"));
        goto Exit;
    }

    while (GiveUpReadBytes(PortType, szAppend, cbAppend))
    {
        if (!cbAppend)
        {
            // Free memory occupied by szAppend
            delete[] szAppend;
            szAppend = NULL;
            break;
        }

        if (!pRsp)
        {
            // Allocate a new response, if we need it
            pRsp = new CResponse(PortType, this);
            if (!pRsp)
            {
                TBD_OUTPUT(TBDCT_INFO, TBDOL_ERROR, TEXT("CRilHandle::ResponseThread : Unable to construct CResponse"));
                // Critically low on memory
                SignalCriticalError(RILLOG_EVENT_LOWMEMORY);
                goto Exit;
            }
        }

        if (m_fLastCommandTimedOut[PortType])
        {
            pRsp->SetPotentialBogusResponseFlag();
            m_fLastCommandTimedOut[PortType] = FALSE;
        }

        if (!pRsp->AppendString(szAppend, cbAppend, szRemainder, cbRemainder))
        {
            TBD_OUTPUT(TBDCT_INFO, TBDOL_ERROR, TEXT("CRilHandle::ResponseThread : AppendString failed"));
            // Critically low on memory
            SignalCriticalError(RILLOG_EVENT_LOWMEMORY);
            goto Exit;
        }

        // Does this contain a complete response?
        if (!szRemainder)
        {
            // No -- break out of this loop and wait for more data to come

            // Move the data back to the Read Bytes buffer
            if (!InheritReadBytes(PortType, pRsp))
            {
                TBD_OUTPUT(TBDCT_INFO, TBDOL_ERROR, TEXT("CRilHandle::ResponseThread : InheritReadBytes failed"));
                // Critically low on memory
                SignalCriticalError(RILLOG_EVENT_LOWMEMORY);
                goto Exit;
            }

            // Free memory occupied by szAppend
            delete[] szAppend;
            szAppend = NULL;
            cbAppend = 0;
            break;
        }
        else
        {

            if (pRsp->FUnrecognized())
            {
                // We couldn't recognize this response -- assume it was corrupted and throw it away
                g_RilLog.LogEvent(RILLOG_ZONE_CMD, RILLOG_EVENT_UNRECOGNIZEDRSP, PrintableString(pRsp->GetData(), pRsp->GetLength()));

                delete pRsp;
                pRsp = NULL;

            }
            else if (!pRsp->FUnsolicited() || (RIL_RESULT_BUSY == pRsp->GetNotifyCode() && FWaitingForRsp(PortType) && m_pCurrCommand[PortType]->FDial()))
            {
                // This is a command result -- is someone waiting for it?
                if (FWaitingForRsp(PortType))
                {
                    // No -- see if it's one of the exceptions, and if not, throw it away
                    if (RIL_RESULT_NOCARRIER == pRsp->GetNotifyCode() && !(m_pCurrCommand[PortType]->FDial() || m_pCurrCommand[PortType]->FAnswer()))
                    {
#ifdef RIL_FAKECSQ
                        g_fFakeSignalStrength = FALSE;
#endif
                        // This is an unsolicited NOCARRIER, the remote end must have hung up -- exit
                        //    out of the data mode and send out a notification
                        if(!g_fGPRSActive)
                        {
                          for (i = 0; i < NUMBER_OF_PORTS; i++)
                          {
                            if (m_pComDevice[(COM_PORT_TYPE)i]->FDataMode())
                        	{
	                            m_pComDevice[(COM_PORT_TYPE)i]->ExitDataMode();
                        	}
                          }
                        }
						
                        if(g_fEmergencyCall) g_fEmergencyCall = FALSE;
                        (void)BroadcastDWORDBlobNotification(RIL_NOTIFY_DISCONNECT, RIL_DISCINIT_REMOTE);
                        TBD_OUTPUT(TBDCT_INFO, TBDOL_INFORMATION, TEXT("CRilHandle::ResponseThread : Detected remote disconnect (AT)"));
                        g_fCallWaiting = FALSE;
                        TestLastActiveCall(this);
                        g_fReleaseHeldCall = FALSE;
                        delete pRsp;
                        pRsp = NULL;
                    }
                    else if (pRsp->GetNotifyCode()==0) {
                        // This might happen, when an unused URC (e.g. RING on a non URC channel) shows
                        // up between a command and the response. In this case we simply
#ifdef TBD_DEBUG
                        RILRetailTrace((TEXT("RilDrv: discarded response: %s\r\n"), VerboseString(TString(pRsp->GetData()))));
#endif
                        delete pRsp;
                        pRsp = NULL;
                    }
                    else
                    {
                        // Yes -- place it into the Response Q
                        if (!QueueRsp(PortType, this, pRsp))
                        {
                            TBD_OUTPUT(TBDCT_INFO, TBDOL_ERROR, TEXT("CRilHandle::ResponseThread : QueueDataRsp failed"));
                            goto Exit;
                        }
                        TBD_ASSERT(NULL == pRsp);
                    }
                }
                else
                {
                    // No -- see if it's one of the exceptions, and if not, throw it away
                    if (RIL_RESULT_NOCARRIER == pRsp->GetNotifyCode())
                    {
                        // This is an unsolicited NOCARRIER, the remote end must have hung up -- exit
                        //    out of the data mode and send out a notification
#ifdef RIL_FAKECSQ
                        g_fFakeSignalStrength = FALSE;
#endif
                        if (!g_fGPRSActive)
                        {
                          for (i = 0; i < NUMBER_OF_PORTS; i++)
                          {
                            if (m_pComDevice[(COM_PORT_TYPE)i]->FDataMode())
                           	{
	                            m_pComDevice[(COM_PORT_TYPE)i]->ExitDataMode();
                           	}
                          }
                        }
                        if (g_fEmergencyCall)
                        {
                            g_fEmergencyCall = FALSE;
                        }

                        // To prevent multiple disconnect notifications we only report notifications received on the URC port.
                        // Exception: if the disconnect is caused be an ATH command on the COMMAND_PORT, the disconnect
                        // notifications had been already sent, so we have to ignore this one.
                        if (PortType == URC_PORT)
                        {
                            if (m_fIgnoreNextNOCARRIER)
                                m_fIgnoreNextNOCARRIER = FALSE;
                            else
                            {
                                (void)BroadcastDWORDBlobNotification(RIL_NOTIFY_DISCONNECT, RIL_DISCINIT_REMOTE);
                            }
                        }
                        TBD_OUTPUT(TBDCT_INFO, TBDOL_INFORMATION, TEXT("CRilHandle::ResponseThread : Detected remote disconnect (AT)"));

                        g_fCallWaiting = FALSE;
                        TestLastActiveCall(this);
                        g_fReleaseHeldCall = FALSE;
                    }
                    else
                    {
#ifdef TBD_DEBUG
                        TBD_OUTPUT(TBDCT_INFO, TBDOL_INFORMATION, TEXT("CRilHandle::ResponseThread : Ignoring a response no one is waiting for: %d chars of %s"), pRsp->GetLength(), VerboseString(TString(pRsp->GetData())));
#endif // TBD_DEBUG
                        g_RilLog.LogEvent(RILLOG_ZONE_CMD, RILLOG_EVENT_NOONEWAITING, PrintableString(pRsp->GetData(), pRsp->GetLength()));
                    }
                    delete pRsp;
                    pRsp = NULL;
                }
            }
            else
            {
                if (RIL_RESULT_BUSY == pRsp->GetNotifyCode())
                {
                    // MC75 Release 2.001  prints out busy on every port, so we ignore all 'BUSY' but the one from the URC_PORT in this case.
                    if (!(g_dwModemType == MODEMTYPE_SIEMENS_MC75 && g_dwModemVersion > 1001) || PortType == URC_PORT)
                    {
                        (void)BroadcastDWORDBlobNotification(RIL_NOTIFY_DISCONNECT, RIL_DISCINIT_BUSY);
                    }
                    TBD_OUTPUT(TBDCT_INFO, TBDOL_INFORMATION, TEXT("CRilHandle::ResponseThread : Detected busy signal"));
                }
                else
                {
                    // This is an unsolicited notification -- broadcast it to all clients
                    // (with the exception of CONNECT's received while in data mode, because they are effectively
                    //  internal to RIL/VSP)
                    if (!m_pComDevice[PortType]->FDataMode() || RIL_NOTIFY_CONNECT != pRsp->GetNotifyCode())
                    {
                        if (pRsp->GetNotifyCode() != 0)     // Filter all URCs without any message.
                        {
                            pRsp->GetBlob(pBlob, cbBlob);
                            (void)BroadcastRealBlobNotification(pRsp->GetNotifyCode(), pBlob, cbBlob);
                            pRsp->DeleteBlob();
                            pBlob = NULL;
                            cbBlob = 0;
                        }
                    }

                    // See if the last command caused us to enter (or return) to data mode
                    if (RIL_NOTIFY_CONNECT == pRsp->GetNotifyCode())
                    {
                        // Yes -- let the virtual serial driver know
                        if (m_pComDevice[PortType]->EnterDataMode(szRemainder, cbRemainder))
                        {
                            szRemainder += cbRemainder;
                            cbRemainder = 0;

                            // Place a response to command that caused connection into the Response Queue
                            //    (the response will later be changed to OK)
                            if (!QueueRsp(PortType, this, pRsp))
                            {
                                TBD_OUTPUT(TBDCT_INFO, TBDOL_ERROR, TEXT("CRilHandle::ResponseThread : QueueDataRsp failed"));
                                goto Exit;
                            }
                            TBD_ASSERT(NULL == pRsp);

                            /*
                            Sleep(10000);
                            if (!EscapeCommFunction(m_pComDevice[PortType]->m_hDownstream, SETDTR))
                            {
                                TBD_OUTPUT(TBDCT_INFO, TBDOL_ERROR, TEXT("CRilHandle::ResponseThread : EscapeCommFunction CLRDTR failed"));
                                goto Exit;
                            }

                            if (!EscapeCommFunction(m_pComDevice[PortType]->m_hDownstream, CLRDTR))
                            {
                                TBD_OUTPUT(TBDCT_INFO, TBDOL_ERROR, TEXT("CRilHandle::ResponseThread : EscapeCommFunction CLRDTR failed"));
                                goto Exit;
                            }

                            Sleep(1000);     

                            DWORD dwMask = 0;

                            if (!WaitCommEvent(m_pComDevice[PortType]->m_hDownstream, &dwMask, NULL))
                            {
                                TBD_OUTPUT(TBDCT_INFO, TBDOL_ERROR, TEXT("CRilHandle::WaitCommEvent : Failed\n"));
                                goto Exit;
                            }

                            RILRetailTrace((TEXT("RilDrv: CComHandle::CheckCmdModeThread : WaitCommEvent Event 0x%x\r\n"), dwMask));

                            if (!GetCommModemStatus(m_pComDevice[PortType]->m_hDownstream, &dwMask))
                            {
                                TBD_OUTPUT(TBDCT_INFO, TBDOL_ERROR, TEXT("CRilHandle::GetCommModemStatus : Failed\n"));
                                goto Exit;
                            }

                            RILRetailTrace((TEXT("RilDrv: CComHandle::CheckCmdModeThread : GetCommModemStatus Status 0x%x\r\n"), dwMask));        
                            */ 

                            TBD_OUTPUT(TBDCT_INFO, TBDOL_INFORMATION, TEXT("CRilHandle::ResponseThread : Waiting for command mode"));

                            // Wait until module drops out of data mode
                            if (!m_pComDevice[PortType]->WaitForCommandMode())
                            {
                                TBD_OUTPUT(TBDCT_INFO, TBDOL_ERROR, TEXT("CRilHandle::ResponseThread : WaitForCommandMode failed"));
                                goto Exit;
                            }
                            TBD_OUTPUT(TBDCT_INFO, TBDOL_INFORMATION, TEXT("CRilHandle::ResponseThread : Done waiting for command mode"));
                        }
                    }
                }
                delete pRsp;
                pRsp = NULL;
            }

            // Append the remaining data to the read bytes buffer
            if (cbRemainder && !AppendReadBytes(PortType, szRemainder, cbRemainder))
            {
                TBD_OUTPUT(TBDCT_INFO, TBDOL_ERROR, TEXT("CRilHandle::ResponseThread : cbRemainder and AppendReadBytes failed"));
                goto Exit;
            }
        }

        // Free memory occupied by szAppend
        delete[] szAppend;
        szAppend = NULL;
        szRemainder = NULL;
        cbAppend = 0;
    }

    bSuccess=TRUE;
Exit:
    if (pRsp) delete pRsp;
    return bSuccess;
}

